package xyz.hubery.task;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.retry.annotation.Retryable;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MimeTypeUtils;
import xyz.hubery.domain.AsyncTask;
import xyz.hubery.domain.EntrustOrder;
import xyz.hubery.dto.FreezeDTO;
import xyz.hubery.dto.LockUserAmountDTO;
import xyz.hubery.enums.AsyncTaskStatus;
import xyz.hubery.enums.AsyncTaskType;
import xyz.hubery.enums.OrderStatus;
import xyz.hubery.feign.AccountServiceFeign;
import xyz.hubery.handler.AsyncTaskHandler;
import xyz.hubery.mapper.AsyncTaskMapper;
import xyz.hubery.mapper.EntrustOrderMapper;
import xyz.hubery.model.Constant;
import xyz.hubery.model.R;
import xyz.hubery.model.TaskResult;
import xyz.hubery.stream.Source;

/**
 * 冻结资金 异步任务处理器
 */
@Component
@Slf4j
public class FreezeBalanceHandler implements AsyncTaskHandler {

    @Autowired
    private AccountServiceFeign accountServiceFeign;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private EntrustOrderMapper entrustOrderMapper;
    @Autowired
    private AsyncTaskMapper asyncTaskMapper;
    @Autowired
    private Source source;

    @Override
    public AsyncTaskType getSupportedType() {
        return AsyncTaskType.LOCK_AMOUNT;
    }

    /**
     * 处理业务逻辑（冻结资金
     *
     * @param task
     * @return
     */
    @Override
    @Transactional
//    @Retryable(value = Exception.class, maxAttempts = Constant.TASK_MAX_RETRY)
    public TaskResult handle(AsyncTask task) {
        // 幂等操作，防止多次操作
        if (Boolean.TRUE.equals(redisTemplate.hasKey(getSupportedType() + ":" + task.getId()))) {
            return TaskResult.success();
        }

        LockUserAmountDTO dto = JSON.parseObject(task.getPayload(), LockUserAmountDTO.class);
        // 获取关联订单
        EntrustOrder order = entrustOrderMapper.selectForUpdate(dto.getOrderId());// 行锁，避免其他线程并发修改

        // 状态检查
        if (order.getStatus() != OrderStatus.CREATING.getCode()) {
            return TaskResult.skipped("订单状态不匹配");
        }

        // 调用资金服务
        R result = accountServiceFeign.lockUserAmount(dto);

        if (result.getCode() != R.ok().getCode()) { // 执行失败
            throw new RuntimeException("资金冻结失败");
        }
        redisTemplate.opsForValue().set(getSupportedType() + ":" + task.getId(), "");
        return TaskResult.success(order);
    }

    @Transactional
    @Override
    public void onSuccess(AsyncTask task, TaskResult result) {
        //更新委托单和异步任务的状态
        EntrustOrder entrustOrder = (EntrustOrder) result.getData();
        int i = entrustOrderMapper.updateStatus(entrustOrder.getId(), OrderStatus.OPEN.getCode());
        int j = asyncTaskMapper.updateStatus(task.getId(), task.getVersion(), AsyncTaskStatus.SUCCESS.getCode());

        if (i <= 0 || j <= 0) { // 操作失败，需要重试
            task.setMsg("委托单更新失败或异步任务更新失败:" + entrustOrder.getId() + " " + task.getId());
            this.onRetry(task);
        }

        // 操作成功
        // 发送消息到消息队列（告知撮合服务匹配订单）
        MessageBuilder<EntrustOrder> builder = MessageBuilder.withPayload(entrustOrder).setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON);
        source.orderOutChannel().send(builder.build());

    }

    @Transactional
    @Override
    public void onFailure(AsyncTask task, TaskResult result) {
        // 重试次数+1，设置错误消息，
        task.setMsg(result.getMessage());
        task.setRetryCount(task.getRetryCount() + 1);
        asyncTaskMapper.failUpdate(task);
        // 重试
        this.onRetry(task);
    }

    // 重试
    @Override
    public void onRetry(AsyncTask task) {
        // 抛出异常
        throw new RuntimeException("异步任务处理失败:"+task.getId() );
    }

    @Override
    public void onSkipped(AsyncTask task, String reason) {
        AsyncTaskHandler.super.onSkipped(task, reason);
    }


}